package hyl.base.net.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import hyl.base.net.IpgReader;
import hyl.core.MyFun;
import hyl.core.run.MyRun;
import hyl.base.net.ISocketSrv;

/*服务器端，:接收客户端发送过来的数据并显示，
 *服务器把上接收到的数据加上"echo from service:"再发送回去*/
public class NIOSocketSrv extends NIOSocket implements ISocketSrv {

	/* 服务器地址 */

	private static final int TimeOut = 3000;
	private static final int I连接数 = 100;

	public NIOSocketSrv(int port) throws IOException {
		ini(new InetSocketAddress(port));
	}

	public NIOSocketSrv(InetSocketAddress socketAddress) {
		ini(socketAddress);
	}

	ServerSocketChannel _sschannel = null;
	Selector _select = null;
	// socket
	Map<SelectableChannel, IpgReader> sockmap = new ConcurrentHashMap<>();

	protected void ini(SocketAddress sa) {
		try {
			/* 创建选择器 */
			_select = Selector.open();

			/* 创建服务器通道 */
			_sschannel = ServerSocketChannel.open();

			/* 调整此通道的阻塞模式。如果为 true，则此通道将被置于阻塞模式；如果为 false，则此通道将被置于非阻塞模式 */
			_sschannel.configureBlocking(false);

			/* 设置监听服务器的端口，设置最大连接缓冲数为100 */
			_sschannel.bind(sa, I连接数);

			/* 服务器通道只能对tcp链接事件感兴趣 */
			_sschannel.register(_select, SelectionKey.OP_ACCEPT);

			MyFun.print("服务器开启监听地址 : ", sa);

		} catch (IOException e1) {
			MyFun.print("服务器开启失败");
			e1.printStackTrace();
			;
			return;
		}
	}

	Thread thread = null;

	public void startServer() {
		if (thread != null && thread.isAlive())
			return;
		thread = MyRun.start用户线程(() -> {
			/* 服务器线程被中断后会退出 */
			try {
				// 轮询select 中的列表
				while (!Thread.currentThread().isInterrupted()) {
					// 等待某个信道就绪
					if (_select.select(TimeOut) == 0) {
						// System.out.println(".");
						continue;
					}
					// 获得就绪信道的键迭代器
					Iterator<SelectionKey> keyIter = _select.selectedKeys().iterator();
					SelectionKey key = null;
					// 使用迭代器进行遍历就绪信道
					// int i=0;

					while (keyIter.hasNext()) {
						key = keyIter.next();
						/* 防止下次select方法返回已处理过的通道 */

						try {
							// 这种情况是有客户端连接过来,准备一个clientChannel与之通信
							if (key.isAcceptable()) {
								SocketChannel sc = ((ServerSocketChannel) key.channel()).accept();
								sc.configureBlocking(false);
								IpgReader reader = new NIOReader(this, sc, _IDo接收处理函数);
								sc.register(_select, SelectionKey.OP_READ, reader);
								// System.out.println("接入: " + sc.getRemoteAddress());
								sockmap.put(sc, reader);
							}
							// 客户端有写入时
							/* （普通）通道感兴趣读事件且有数据可读 */
							else if (key.isReadable()) {
								// 得到并重置缓冲区的主要索引值
								// System.out.println("写入完毕"+i);
								read一次(key);
								// i++;
								// System.out.println("接收次数"+i);
								// System.out.println("写入完毕"+i);
							}

						} catch (IOException e) {
							key.cancel();
							SelectableChannel sc = key.channel();
							IpgReader reader = sockmap.get(sc);
							if (reader != null) {
								if (_IDo断开处理函数 != null) {
									_IDo断开处理函数.run(reader);
								}
								reader.close();
							}						
						}
						keyIter.remove();
					}
				}
			} catch (IOException e1) {
				// e1.printStackTrace();
			} finally {
				try {
					sockmap.clear();
					_select.close();
					_sschannel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
	}

	@SuppressWarnings("null")
	/**
	 * 
	 * @param 类型
	 * @param 数据包
	 * @param 接收人群
	 * @return 接收人数
	 */
	public static int sendMany(byte 类型, byte[] 数据包, List<SocketChannel> 接收人群) {
		if (数据包 != null || 接收人群 != null)
			return 0;
		int flag = 0;
		try {
			NIOData1 dp = new NIOData1();
			dp.data = 数据包;
			dp.i类型 = 类型;
			for (SocketChannel sc : 接收人群) {
				try {
					send(sc, dp);
				} catch (Exception e) {

				}
				flag++;
			}
			return flag;
		} catch (Exception e) {
			e.printStackTrace();
			return flag;
		}
	}

	/**
	 * 发送给连接服务端口的所有人
	 * 
	 * @param 数据包
	 * @return 接收人数
	 * @throws IOException
	 */
	public int sendAll(byte 类型, byte[] 数据包) {
		return sendEx(null, 类型, 数据包);
	}

	/**
	 * 发送给出来 排除
	 * 
	 * @param 数据包
	 * @return 接收人数
	 * @throws IOException
	 */
	public int sendEx(IpgReader 排除他, byte 类型, byte[] 数据包) {
		if (数据包 != null && _select == null)
			return 0;
		int count = 0;

		try {
			if (_select.selectNow() == 0) {
				return 0;
			}
		} catch (IOException e1) {
			return count;
		}
		try {
			NIOData1 dp = new NIOData1();
			dp.data = 数据包;
			dp.i类型 = 类型;
			ByteBuffer bb = dp.getSendBytes();
			// 广播数据到所有的SocketChannel中
			for (SelectionKey key : _select.keys()) {
				if (!key.isValid())
					continue;
				// Channel targetchannel = key.channel();
				IpgReader reader = (IpgReader) key.attachment();
				// 如果except不为空，不回发给发送此内容的客户端
				if (reader != 排除他) {
					reader.send(bb);
					count++;
				}
			}
			return count;
		} catch (Exception e) {
			return count;
		}
	}

	@Override
	public void close() {
		// TODO Auto-generated method stub
		thread.interrupt();
	}

}
